package main

import (
	"common/cfgstruct"
	"common/log"
	"common/process"
	"common/run"
	"common/uuid"
	"common/version"
	"encoding/json"
	"enode"
	"enode/contact"
	"enode/db"
	"enode/p2pServer"
	"enode/repair"
	"enode/store"
	"fmt"
	"github.com/spf13/cobra"
	"github.com/wuzfei/go-helper/path"
	"io/fs"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

type Config struct {
	NodeId  string `user:"true"`
	DevId   string `user:"true"`
	Contact contact.Config
	Store   enode.Config
	Log     log.Config
	DB      db.Config
	Repair  repair.Config
}

var (
	configFile string
	rootCmd    = &cobra.Command{
		Use:   "enode",
		Short: "分布式存储-节点服务",
	}
	runCmd = &cobra.Command{
		Use:   "run",
		Short: "运行",
		RunE:  cmdRun,
	}
	setupCmd = &cobra.Command{
		Use:         "setup",
		Short:       "初始化配置文件",
		RunE:        cmdSetup,
		Annotations: map[string]string{"type": "setup"},
	}
	configCmd = &cobra.Command{
		Use:   "config",
		Short: "查看当前所有配置",
		RunE:  cmdConfig,
	}
	versionCmd = &cobra.Command{
		Use:   "version",
		Short: "查看当前版本",
		RunE:  cmdVersion,
	}
	runConfig   Config
	setupConfig Config
)

func main() {
	defaultConfig := path.ApplicationDir("enode", process.DefaultCfgFilename)
	cfgstruct.SetupFlag(rootCmd, &configFile, "config", defaultConfig, "配置文件")
	//根据环境读取默认配置
	defaults := cfgstruct.DefaultsFlag(rootCmd)
	currentDir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
	//程序目录
	rootDir := cfgstruct.ConfigVar("ROOT", currentDir)
	//设置系统的HOME变量
	envHome := cfgstruct.ConfigVar("HOME", os.Getenv("HOME"))
	rootCmd.AddCommand(runCmd)
	rootCmd.AddCommand(setupCmd)
	rootCmd.AddCommand(configCmd)
	rootCmd.AddCommand(versionCmd)
	process.Bind(runCmd, &runConfig, defaults, cfgstruct.ConfigFile(configFile), envHome, rootDir)
	process.Bind(configCmd, &runConfig, defaults, cfgstruct.ConfigFile(configFile), envHome, rootDir)
	process.Bind(versionCmd, &runConfig, defaults, cfgstruct.ConfigFile(configFile), envHome, rootDir)
	process.Bind(setupCmd, &setupConfig, defaults, cfgstruct.ConfigFile(configFile), envHome, cfgstruct.SetupMode(), rootDir)
	process.Exec(rootCmd)
}

func cmdRun(cmd *cobra.Command, args []string) error {
	ctx, _ := process.Ctx(cmd)
	runConfig.Contact.DevId = runConfig.DevId
	runConfig.Contact.NodeId = runConfig.NodeId
	//installPgServer(runConfig.LocalDeviceId)

	if err := os.MkdirAll(runConfig.Store.PiecePath, fs.ModePerm); err != nil {
		panic(err)
	}

	log2 := log.NewZapLog(&runConfig.Log)
	db2, err := db.NewDB(runConfig.DB.Database, log2, runConfig.DB.LogLevel)
	if err != nil {
		panic(err)
	}
	_store := store.NewStore(ctx, log2.Named("store"), runConfig.Store.StoreMode, runConfig.Store.PiecePath)

	p2pServer.Server(ctx, runConfig.DevId)
	centerEndpoint := contact.NewEndpoint(db2, _store)
	rGroup := run.NewGroup()
	//contact 服务
	log2.Info("##################  restart ")

	rGroup.Add(contact.NewService(log2.Named("contact"), db2, runConfig.Contact, centerEndpoint, runConfig.Store.PiecePath))
	//drpc服务
	//rGroup.Add(enode.NewPiecestoreServer(log2.Named("drpc"), runConfig.Store, _store))

	nodeId, err := uuid.FromString(runConfig.NodeId)
	if err != nil {
		return err
	}

	rGroup.Add(repair.NewService(log2.Named("repair"), runConfig.Repair, nodeId))

	rGroup.Add(enode.NewERPCServer(log2.Named("erpc"), runConfig.Store, _store))
	return rGroup.Run(ctx)
}

// 如果硬盘有devid,使用硬盘的
func replaceDevIdFromLocal() {
	const devIdFile = "/usr/local/bin/p2puid"
	data, err := ioutil.ReadFile(devIdFile)
	if err != nil {
		return
	}
	devid := string(data)
	devid = strings.TrimSpace(devid)
	devid = strings.Trim(devid, "\r")
	devid = strings.Trim(devid, "\n")
	setupConfig.DevId = devid
}
func cmdSetup(cmd *cobra.Command, args []string) error {
	nodeId, _ := uuid.New()
	setupConfig.NodeId = nodeId.String()
	replaceDevIdFromLocal()
	return process.SaveConfig(cmd, configFile)
}

func cmdConfig(cmd *cobra.Command, args []string) error {
	fmt.Printf("当前运行环境：[%s]\n", cfgstruct.DefaultsType())
	fmt.Println("当前配置文件路径：", configFile)
	output, _ := json.MarshalIndent(runConfig, "", " ")
	fmt.Println(string(output))
	return nil
}

func cmdVersion(cmd *cobra.Command, args []string) error {
	data := map[string]any{
		"node_id":     runConfig.NodeId,
		"version":     version.Build.Version,
		"commit_hash": version.Build.CommitHash,
		"timestamp":   version.Build.Timestamp,
		"release":     version.Build.Release,
	}
	res, _ := json.Marshal(data)
	fmt.Println(string(res))
	return nil
}
